<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>AI 对话助手</title>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3b82f6', // blue-500
                        assistant: '#e0f2fe', // light blue background
                    }
                }
            }
        }
    </script>
    <style>
        /* 确保 EventSource 接收的是 text/event-stream，但 Spring AI 默认返回 JSON 数组？ */
        /* 注意：Spring AI 的 Flux<ChatResponse> 默认不是 SSE 格式！需后端适配为 text/event-stream */
        /* 本代码假设后端已按 SSE 格式返回：data: { ... }\n\n */
    </style>
</head>
<body class="bg-gray-50 min-h-screen flex flex-col">
<div class="max-w-3xl w-full mx-auto p-4 flex flex-col flex-grow">
    <h1 class="text-2xl font-bold text-center mb-6 text-gray-800">AI 对话助手</h1>

    <!-- 聊天区域 -->
    <div id="chatContainer" class="flex-grow overflow-y-auto mb-4 space-y-4 p-2 bg-white rounded-lg shadow-sm">
        <!-- 消息将动态插入此处 -->
    </div>

    <!-- 输入区域 -->
    <div class="flex gap-2">
        <input
                id="messageInput"
                type="text"
                placeholder="请输入消息..."
                class="flex-grow px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
                autocomplete="off"
        />
        <button
                id="sendButton"
                class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-400"
        >
            发送
        </button>
    </div>
</div>

<script>
    const chatContainer = document.getElementById('chatContainer');
    const messageInput = document.getElementById('messageInput');
    const sendButton = document.getElementById('sendButton');

    // 添加用户消息到聊天区
    function addUserMessage(text) {
        const div = document.createElement('div');
        div.className = 'flex justify-end';

        const bubble = document.createElement('div');
        bubble.className = 'max-w-xs md:max-w-md bg-gray-200 text-gray-800 p-3 rounded-lg';
        bubble.innerHTML = escapeHtml(text); // 注意：这里用 innerHTML 因为 escapeHtml 返回字符串

        div.appendChild(bubble);
        chatContainer.appendChild(div);
        scrollToBottom();
    }

    // 添加 AI 消息（支持流式更新）
    function addAssistantMessage() {
        const div = document.createElement('div');
        div.className = 'flex justify-start';

        const bubble = document.createElement('div');
        bubble.className = 'max-w-xs md:max-w-md bg-blue-50 text-gray-800 p-3 rounded-lg';
        bubble.textContent = '';

        div.appendChild(bubble);
        chatContainer.appendChild(div);
        scrollToBottom();
        return bubble;
    }

    function scrollToBottom() {
        chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    function escapeHtml(text) {
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, m => map[m]);
    }

    // 处理发送逻辑
    function handleSend() {
        const message = messageInput.value.trim();
        if (!message) return;

        messageInput.value = '';
        sendButton.disabled = true;
        sendButton.textContent = '发送中...';

        addUserMessage(message);
        const aiBubble = addAssistantMessage();

        const eventSource = new EventSource('/api/v1/ollama/generate_stream?message=' + encodeURIComponent(message) + '&model=deepseek-r1:1.5b');

        let isClosed = false;

        function cleanup() {
            if (isClosed) return;
            isClosed = true;
            sendButton.disabled = false;
            sendButton.textContent = '发送';
        }

        eventSource.onmessage = (event) => {
            if (isClosed) return;

            try {
                const data = JSON.parse(event.data);
                const content = data?.result?.output?.text || '';

                if (!content || ['<think>', 'void', ''].includes(content.trim()) ||
                    (content.startsWith('<|') && content.endsWith('|>'))) {
                    return;
                }

                aiBubble.textContent += content;
                scrollToBottom();

                if (data?.result?.metadata?.finishReason === 'STOP') {
                    cleanup();
                    eventSource.close();
                }
            } catch (e) {
                console.error('JSON解析失败:', e);
                if (!isClosed) aiBubble.textContent += '\n[解析错误]';
            }
        };

        eventSource.onerror = (err) => {
            // ✅ 核心修复：只要连接已关闭，就当正常结束
            if (eventSource.readyState === EventSource.CLOSED) {
                console.log('ℹ️ SSE 正常结束（连接已关闭）');
                cleanup();
                return;
            }

            // 真正的网络错误
            console.error('🔴 SSE 异常:', err);
            aiBubble.textContent += '\n[连接失败，请检查控制台]';
            cleanup();
            eventSource.close();
        };

        eventSource.onopen = () => {
            console.log('✅ SSE 连接已建立');
        };
    }

    sendButton.addEventListener('click', handleSend);
    messageInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') handleSend();
    });
</script>
</body>
</html>